package org.mmy.mmybackend.ServerFileTransfer.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
//import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.mmy.mmybackend.ServerFileTransfer.entity.File;
import org.mmy.mmybackend.ServerFileTransfer.service.FileService;
import org.mmy.mmybackend.ServerFileTransfer.service.FileStorageService;
import org.mmy.mmybackend.util.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Controller
@RequestMapping("/file")
@CrossOrigin(origins = "*") // 允许所有来源（开发环境使用，生产环境建议指定具体域名）
@RequiredArgsConstructor
public class FileController {

    private static final Logger logger = LoggerFactory.getLogger(FileController.class);

    @javax.annotation.Resource // 保持与您代码一致
    private FileStorageService fileStorageService;

    private final FileService fileService;

    /**
     * 处理分块文件上传的端点
     *
     * @param file             文件块
     * @param fileIdentifier   整个文件的唯一标识符 (例如：文件名-文件大小-最后修改时间戳)
     * @param chunkNumber      当前块的编号 (从0开始)
     * @param totalChunks      总块数
     * @param originalFilename 原始文件名
     * @return Result 对象
     */
    @PostMapping("/upload")
    @ResponseBody // 确保返回的是JSON
    public Result uploadChunk(
            @RequestParam("file") MultipartFile file,
            @RequestParam("fileIdentifier") String fileIdentifier,
            @RequestParam("chunkNumber") int chunkNumber,
            @RequestParam("totalChunks") int totalChunks,
            @RequestParam("originalFilename") String originalFilename) {

        // 在实际调用服务前，可以添加 fileStorageService.init() 的检查，
        // 但通常在 @PostConstruct 中初始化失败应该有更全局的处理。
//        if (!fileStorageService.init()){
//            return Result.FAIL("初始化失败无法上传",500);
//        }

        try {
            CompletableFuture<String> future = fileStorageService.storeChunkAndMerge(
                    file, fileIdentifier, chunkNumber, totalChunks, originalFilename
            );

            // 可选：设置超时时间，避免长时间等待
            String mergedFilePath = future.get(30, TimeUnit.SECONDS);

            if (mergedFilePath != null) {
                // 所有块已上传并合并成功
                java.io.File  rename = new java.io.File(mergedFilePath);
                String fileExtension = fileStorageService.getExtension(originalFilename);
                String uniqueFileName = UUID.randomUUID().toString() +
                        (fileExtension != null ? "." + fileExtension : "");
                // 构建新的文件路径
                Path directory = Paths.get(rename.getParent());
                Path newFilePath = directory.resolve(uniqueFileName);

                // 重命名文件
                Files.move(rename.toPath(), newFilePath, StandardCopyOption.REPLACE_EXISTING);

                String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                        .path("/file/download/") // 假设你有一个下载端点
                        .path(uniqueFileName) // 或者使用某种方式从mergedFilePath提取文件名
                        .toUriString();

                // 返回一个包含文件URI和状态的对象
                File sqlfile = new File();
                sqlfile.setFilename(fileStorageService.getMainFileName(originalFilename));
                sqlfile.setFileUrl(fileDownloadUri);
                sqlfile.setFiletype(fileStorageService.getExtension(originalFilename));
                sqlfile.setFilesize(String.valueOf(file.getSize()));
                sqlfile.setUploadtime(java.time.LocalDateTime.now());
                fileService.save(sqlfile);
                return Result.SUCCESS(fileDownloadUri, "文件上传并合并成功: " + originalFilename);
            } else {
                // 块已上传，但文件尚未合并完成
                return Result.SUCCESS("Chunk " + chunkNumber + "/" + (totalChunks -1) + " for " + originalFilename + " uploaded successfully. Waiting for remaining chunks.", "块上传成功");
            }
        } catch (IllegalArgumentException e) {
            logger.warn("Illegal argument during chunk upload: {}", e.getMessage());
            return Result.FAIL("上传参数错误: " + e.getMessage(), 400); // Bad Request
        } catch (IOException e) {
            logger.error("Error during chunk upload or merge for identifier {}: {}", fileIdentifier, e.getMessage(), e);
            return Result.FAIL("块上传或合并失败: " + e.getMessage(), 500);
        } catch (Exception e) {
            logger.error("Unexpected error during chunk upload for identifier {}: {}", fileIdentifier, e.getMessage(), e);
            return Result.FAIL("上传过程中发生未知错误: " + e.getMessage(), 500);
        }
    }


    /**
     * 文件下载接口
     *
     * @param filename 文件名
     * @param request  HttpServletRequest 用于动态获取MIME类型
     * @return ResponseEntity 包含文件资源
     */
    @GetMapping("/download/{filename:.+}") // :.+ 用于匹配带有点号的文件名
    public ResponseEntity<Resource> downloadFile(@PathVariable String filename, HttpServletRequest request) {
        // 从 FileStorageService 获取最终文件存储的根路径
        // 假设 FileStorageService 有一个方法可以获取 finalUploadPath
        // 或者直接在这里构造，但更好的做法是从service获取或配置中读取
        Path fileStorageLocation = Paths.get(fileStorageService.getFinalUploadPathString()); // 你需要在FileStorageService中添加此方法
        Path filePath = fileStorageLocation.resolve(filename).normalize();

        try {
            Resource resource = new UrlResource(filePath.toUri());
            if (resource.exists() && resource.isReadable()) {
                // 尝试确定文件的内容类型
                String contentType = null;
                try {
                    // contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath()); // 可能因安全限制不可用
                    contentType = Files.probeContentType(filePath); // Java 7+ 推荐
                } catch (IOException ex) {
                    logger.info("Could not determine file type for {}.", filename);
                }

                // 如果无法确定，则使用默认的二进制流类型
                if (contentType == null) {
                    contentType = "application/octet-stream";
                }

                logger.info("Downloading file: {} with content type: {}", filename, contentType);

                return ResponseEntity.ok()
                        .contentType(MediaType.parseMediaType(contentType))
                        // Content-Disposition 使得浏览器提示下载而不是直接显示（对于某些类型如图片/PDF）
                        // attachment 表示下载，inline 表示尝试内联显示
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                        // .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + resource.getFilename() + "\"") // 如果想尝试内联显示
                        .body(resource);
            } else {
                logger.warn("File not found or not readable: {}", filename);
                // 可以返回自定义的错误Result对象，或者标准的404
                // return ResponseEntity.notFound().build();
                // 为了统一，可以返回Result对象，但下载通常直接返回ResponseEntity
                return ResponseEntity.status(404).body(null); // 或者一个错误提示的 Resource
            }
        } catch (MalformedURLException ex) {
            logger.error("Error creating URL for file: {}", filename, ex);
            // return ResponseEntity.badRequest().build(); // 或者更详细的错误
            return ResponseEntity.status(500).body(null);
        }
    }






    //增加
//    @ApiOperation(value = "File新增")
//    @PostMapping("/insert")
//    public boolean add(@RequestBody File file) {
//        return fileService.save(file);
//    }

//    @ApiOperation(value = "File条件查询")
    @PostMapping("/select")
    @ResponseBody
    public List<File> getById(@RequestBody File file) {
        QueryWrapper<File> queryWrapper = new QueryWrapper<>();
        if(file.getId() != null) queryWrapper.eq("id", file.getId());
        if (file.getFilename() != null) queryWrapper.like("filename", file.getFilename());
        if (file.getFiletype() != null) queryWrapper.like("filetype", file.getFiletype());
        if (file.getUploadtime() != null) queryWrapper.eq("uploadtime", file.getUploadtime());
        return fileService.list(queryWrapper);
    }

    //删除
//    @ApiOperation(value = "File通过id删除")
    @DeleteMapping("/delete/{id}")
    @ResponseBody
    public Result deleteById(@PathVariable("id") Integer id) {
         String filename=fileService.getById(id).getFilename(); // Commented out
//        String  filename="CentOS-8.5.2111-x86_64-dvd1"; // This is the base name
        if(fileService.removeById(id)==false){
            return Result.FAIL("数据库删除失败", 500);
        }
        try {
            // 3. 删除物理文件 (deleteFileByName 会删除所有匹配基本名称的文件)
            boolean physicalFileDeleted = fileStorageService.deleteFileByName(filename);
            // System.out.println("fileStorageService.deleteFileByName(baseFilename) result = " + physicalFileDeleted); // for debugging

            if (physicalFileDeleted) {
                logger.info("基于基本名称 '{}' 的物理文件已成功删除或不存在。", filename);
            } else {
                // 这可能意味着文件已手动删除，或者从未正确上传到该位置。
                // 对于删除操作，如果文件不存在，通常也视为“成功”或至少不是一个阻止数据库记录删除的硬错误。
                logger.warn("未找到基于基本名称 '{}' 的物理文件，或删除失败。可能文件已不存在。", filename);
            }

            // 4. 无论物理文件是否实际删除成功（因为它可能本就不存在），都尝试删除数据库记录

            return Result.SUCCESS(null, "本地删除成功");
        } catch (IOException e) {
            logger.error("删除物理文件 (基本名: '{}') 过程中发生IO异常: {}", filename, e.getMessage(), e);
            return Result.FAIL("删除物理文件时出错: " + e.getMessage(), 500);
        } catch (IllegalArgumentException e) {
            logger.error("删除文件时参数错误 (基本名: '{}'): {}", filename, e.getMessage(), e);
            return Result.FAIL("删除文件参数错误: " + e.getMessage(), 400);
        }
    }
}
